#pragma once

#include "Epoller.hpp"
#include "Common.hpp"
#include "Log.hpp"
#include "Connection.hpp"

using namespace EpollModule;
using namespace LogModule;
// 管理链接
using connection_t = std::shared_ptr<Connection>;

const static int eventnum = 64;

class Reactor
{
public:
    Reactor()
        :_epoller(std::make_unique<Epoller>())
        ,_isrunning(false)
    {
        _epoller->Init();
    }

    void DebugPrint()
    {
        LOG(LogLevel::DEBUG) << "Epoller管理的fd: ";
        for(auto& e : _connection)
            std::cout << e.first << ' ';
        std::cout << std::endl;
    }

    // 插入节点fd，新来的fd也要管理起来
    void InsertConnection(connection_t conn)
    {
        if(!_connection.contains(conn->Sockfd()))
        {
            // 1.把链接放到unordered_map中管理
            _connection[conn->Sockfd()] = conn;
            // 2.将新插入进来的链接，写到内核里（插入底层红黑树中）
            _epoller->Add(conn->Sockfd(), conn->GetEvents());
            // 3. 设置回指epollserver的指针owner，对每一个Connection都提供这个功能
            conn->SetOwner(this);
            LOG(LogLevel::DEBUG) << "add connection success: " << conn->Sockfd();
        }
    }

    // 对哪个文件描述符，更改所需要的事件关心，
    void EnableReadWrite(int sockfd, bool readable, bool writeable)
    {
        if(_connection.contains(sockfd))
        {
            // 1. 更改事件关心
            uint32_t events = ((readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET);
            _connection[sockfd]->SetEvents(events);
            // 2. 写透到内核中
            _epoller->Update(sockfd, _connection[sockfd]->GetEvents());
        }
    }

    // 删除特定文件描述符的连接：因为出现异常了
    void DelConnection(int sockfd)
    {
        if(_connection.contains(sockfd))
        {
            // 1. 从内核中取消对该sockfd的关心
            _epoller->Delete(sockfd);
            // 2. 关闭该文件描述符
            _connection[sockfd]->Close();
            // 3. 从_connection中移除连接
            _connection.erase(sockfd);
        }
    }

    // 对已经就绪的事件进行派发！
    // 基于事件驱动的事件派发器
    void Dispatcher(int n)
    {
        if(n < 0)
            return;
        for(int i = 0;i < n;i++)
        {
            // 开始进行派发，派发到对应的模块
            int sockfd = _revs[i].data.fd;
            uint32_t revents = _revs[i].events;
            if((revents & EPOLLERR) || (revents & EPOLLHUP))
                revents = EPOLLIN | EPOLLOUT;  // 将异常事件转变成读+写事件？？？
            if((revents & EPOLLIN) && _connection.contains(sockfd))  // 处理读事件
            {
                // 读事件就绪，是哪一个读事件就绪了呢？将自己传进来
                _connection[sockfd]->Recver();
            }
            if((revents & EPOLLOUT) && _connection.contains(sockfd))  // 处理写事件
            {
                _connection[sockfd]->Sender();
            }
        }
    }

    void LoopOnce(int timeout)
    {
        int n = _epoller->Wait(_revs, eventnum, timeout);  // 返回了就绪事件的个数
        Dispatcher(n);  // 派发事件
    }

    void Loop()
    {
        _isrunning = true;
        int timeout = -1;
        while(_isrunning)
        {
            /* 整个的逻辑是：比如listensockfd，先进行wait获取sockfd和revents，然后处理事件，比如listensockfd关心读事件，就会调用CallRecv，就会到对应的
Connection中的回调方法recver，而listen的回调方法在 EpollMain.cc 中就设置了，就是会调用Accepter()，然后执行accept，获取链接，处理事件，这就是回调函数的作用 */
            
            LoopOnce(timeout);  // 执行一次循环
            DebugPrint();
        }
        _isrunning = false;
    }

    void Stop()
    {
        _isrunning = false;
    }

    ~Reactor()
    {}
private:
    std::unique_ptr<Epoller> _epoller;  // 封装的epoll, 在这里只定义了智能指针对象，必须初始化
    bool _isrunning;
    std::unordered_map<int, connection_t> _connection;  // 服务器内部所有的链接，管理fd（键值：fd : Connection）
    struct epoll_event _revs[eventnum];  // 管理事件
};